﻿using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Win32;
using Sample.JwtBearer.Api.Data;
using Sample.JwtBearer.Api.Requests;
using Sample.JwtBearer.Api.Settings;

namespace Sample.JwtBearer.Api.Services;

public class UserService : IUserService
{
    private readonly UserManager<AppUser> _userManager;
    private readonly JwtSettings _jwtSettings;
    private readonly AppJwtBearerDbContext _appDbContext;

    public UserService(UserManager<AppUser> userManager, JwtSettings jwtSettings, AppJwtBearerDbContext appDbContext)
    {
        _userManager = userManager;
        _jwtSettings = jwtSettings;
        _appDbContext = appDbContext;
    }

    public async Task<TokenResult> RegisterAsync(RegisterRequest register)
    {
        var existingUser = await _userManager.FindByNameAsync(register.UserName);
        if (existingUser != null)
        {
            return new TokenResult()
            {
                Errors = new[] { "user already exists!" }, //用户已存在
            };
        }

        var newUser = new AppUser()
        {
            Id = Guid.NewGuid().ToString("N"),
            UserName = register.UserName,
            Account = register.Account,
            Email = register.Email,
            PhoneNumber = register.PhoneNumber,
            PasswordHash = GetHashString(register.Password),
            Address = register.Address
        };

        //string pwd = "存储 password 的 md5 值";
        var isCreated = await _userManager.CreateAsync(newUser, register.Password);
        if (!isCreated.Succeeded)
        {
            return new TokenResult()
            {
                Errors = isCreated.Errors.Select(p => p.Description)
            };
        }

        return await GenerateJwtToken(newUser);
    }

    public async Task<TokenResult> LoginAsync(LoginRequest login)
    {
        /*
        var myUser = new AppUser()
        {
            UserName = login.UserName,
            Account = login.Account,
            PasswordHash = GetHashString(login.Password)
        };

        var userLoginInfos = await _userManager.GetLoginsAsync(myUser);
        */

        var existingUser = await _userManager.FindByNameAsync(login.UserName);
        if (existingUser == null)
        {
            return new TokenResult()
            {
                Errors = new[] { "user does not exist!" }, //用户不存在
            };
        }

        var isCorrect = await _userManager.CheckPasswordAsync(existingUser, login.Password);
        if (!isCorrect)
        {
            return new TokenResult()
            {
                Errors = new[] { "wrong user name or password!" }, //用户名或密码错误
            };
        }

        return await GenerateJwtToken(existingUser);
    }

    public async Task<TokenResult> RefreshTokenAsync(RefreshTokenRequest request)
    {
        var (state, claimsPrincipal, msg) = GetClaimsPrincipalByToken(request.AccessToken);
        if (state == false || claimsPrincipal == null)
        {
            // 无效的token...
            return new TokenResult()
            {
                Errors = new[] { "1: Invalid request!" },
            };
        }

        var expiryDateUnix = long.Parse(claimsPrincipal.Claims.Single(x => x.Type == JwtRegisteredClaimNames.Exp).Value);
        var expiryDateTimeUtc = UnixTimeStampToDateTime(expiryDateUnix);
        if (expiryDateTimeUtc > DateTime.UtcNow)
        {
            // token未过期...
            return new TokenResult()
            {
                Errors = new[] { "2: Invalid request!" },
            };
        }

        var jti = claimsPrincipal.Claims.Single(x => x.Type == JwtRegisteredClaimNames.Jti).Value;

        var storedRefreshToken = await _appDbContext.RefreshTokens.SingleOrDefaultAsync(x => x.Token == request.RefreshToken);
        if (storedRefreshToken == null)
        {
            // 无效的refresh_token...
            return new TokenResult()
            {
                Errors = new[] { "3: Invalid request!" },
            };
        }

        if (storedRefreshToken.ExpiryTime < DateTime.UtcNow)
        {
            // refresh_token已过期...
            return new TokenResult()
            {
                Errors = new[] { "4: Invalid request!" },
            };
        }

        if (storedRefreshToken.Invalidated)
        {
            // refresh_token已失效...
            return new TokenResult()
            {
                Errors = new[] { "5: Invalid request!" },
            };
        }

        if (storedRefreshToken.Used)
        {
            // refresh_token已使用...
            return new TokenResult()
            {
                Errors = new[] { "6: Invalid request!" },
            };
        }

        if (storedRefreshToken.JwtId != jti)
        {
            // refresh_token与此token不匹配...
            return new TokenResult()
            {
                Errors = new[] { "7: Invalid request!" },
            };
        }

        storedRefreshToken.Used = true;
        //_userDbContext.RefreshTokens.Update(storedRefreshToken);
        await _appDbContext.SaveChangesAsync();

        var dbUser = await _userManager.FindByIdAsync(storedRefreshToken.UserId);
        return await GenerateJwtToken(dbUser);
    }

    private (bool state, ClaimsPrincipal? claimsPrincipal, string msg) GetClaimsPrincipalByToken(string token)
    {
        try
        {
            var key = Encoding.UTF8.GetBytes(_jwtSettings.SecurityKey);
            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true, // 验证Token颁发机构
                ValidateAudience = true, // 验证颁发给谁
                ValidateIssuerSigningKey = true, // 验证签名秘钥
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ClockSkew = TimeSpan.Zero,
                ValidateLifetime = false, // 不验证过期时间！！！
                ValidIssuer = _jwtSettings.Issuer,
                ValidAudience = _jwtSettings.Audience
            };

            var jwtTokenHandler = new JwtSecurityTokenHandler();
            var claimsPrincipal = jwtTokenHandler.ValidateToken(token, tokenValidationParameters, out SecurityToken validatedToken);
            var validatedSecurityAlgorithm = validatedToken is JwtSecurityToken jwtSecurityToken
                                             && jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase);

            //return validatedSecurityAlgorithm ? claimsPrincipal : null;
            return (validatedSecurityAlgorithm, claimsPrincipal, "ok");
        }
        catch (Exception ex)
        {
            //日志记录,无效的token...
            string error = ex.Message;
            return (false, null, error);
        }
    }

    private async Task<TokenResult> GenerateJwtToken(AppUser user)
    {
        var key = Encoding.UTF8.GetBytes(_jwtSettings.SecurityKey);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
            {
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString("N")),
                new Claim(JwtRegisteredClaimNames.Sub, user.Id),
                new Claim(JwtRegisteredClaimNames.Iss, _jwtSettings.Issuer), //Issuer：Token颁发机构
                new Claim(JwtRegisteredClaimNames.Aud, _jwtSettings.Audience) //Audience：颁发给谁
            }),
            IssuedAt = DateTime.UtcNow,
            NotBefore = DateTime.UtcNow,
            Expires = DateTime.UtcNow.Add(_jwtSettings.ExpiresIn),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
        };

        var jwtTokenHandler = new JwtSecurityTokenHandler();
        var securityToken = jwtTokenHandler.CreateToken(tokenDescriptor);
        var token = jwtTokenHandler.WriteToken(securityToken);

        var refreshToken = new RefreshToken()
        {
            Id = Guid.NewGuid().ToString("N"),
            JwtId = securityToken.Id,
            UserId = user.Id,
            CreationTime = DateTime.UtcNow,
            ExpiryTime = DateTime.UtcNow.AddDays(_jwtSettings.ExpiryTimeOfDay), // DateTime.UtcNow.AddMonths(6),
            Token = GenerateRandomNumber(),
            AccessTokenMd5 = token, // 存储访问 Token 的 MD5 值
        };

        await _appDbContext.RefreshTokens.AddAsync(refreshToken);
        await _appDbContext.SaveChangesAsync();

        return new TokenResult()
        {
            AccessToken = token,
            TokenType = "Bearer",
            ExpiresIn = (int)_jwtSettings.ExpiresIn.TotalSeconds,
            RefreshToken = refreshToken.Token,
            ExpiryTimeOfDay = _jwtSettings.ExpiryTimeOfDay
        };
    }

    private string GenerateRandomNumber(int len = 32)
    {
        var randomNumber = new byte[len];
        using var rng = RandomNumberGenerator.Create();
        rng.GetBytes(randomNumber);
        return Convert.ToBase64String(randomNumber);
    }

    private DateTime UnixTimeStampToDateTime(long unixTimeStamp)
    {
        var dateTimeVal = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
        dateTimeVal = dateTimeVal.AddSeconds(unixTimeStamp).ToUniversalTime();

        return dateTimeVal;
    }


    #region 获取字符串的 MD5 hash 值
    private string GetHashString(string str)
    {
        byte[] tmpSource = Encoding.UTF8.GetBytes(str);
        var hash = MD5.Create();
        byte[] tmpHash = hash.ComputeHash(tmpSource);
        return ByteArrayToString(tmpHash);
    }

    static string ByteArrayToString(byte[] arrInput)
    {
        var sOutput = new StringBuilder(arrInput.Length);
        for (int i = 0; i < arrInput.Length - 1; i++)
        {
            sOutput.Append(arrInput[i].ToString("X2"));
        }
        return sOutput.ToString();
    } 
    #endregion

}